#include "tick.h"

#include <assert.h>
#include <time.h>
#ifdef _WIN32
# include <windows.h>
#endif

sc_tick
sc_tick_now(void) {
#ifndef _WIN32

    // struct timespec有两个成员，一个是秒，另一个秒数之后的纳秒, 所以最高精确度是纳秒。
    // timespec只适用于持续时间和时间戳等没有时区概念的场景，无法表示跨越多个时区的时间;
    // Maximum sc_tick precision (microsecond)
    struct timespec ts;

    // 函数"clock_gettime"是基于Linux C语言的时间函数,他可以用于计算精度和纳秒。
    // CLOCK_MONOTONIC:从系统启动这一刻起开始计时,不受系统时间被用户改变的影响
    // clock_gettime 比 gettimeofday , clock 计时更加精确
    // clock_gettime 返回0 表示获取成功，非0 表示返回时间失败
    // clock_gettime效率如何，是否为线程安全的？

    int ret = clock_gettime(CLOCK_MONOTONIC, &ts);
    if (ret) {
        // abort 函数的作用是异常终止一个进程，意味着 abort 后面的代码将不再执行。
        // abort 函数是一个比较严重的函数，当调用它时，会导致程序异常终止，而不会进行一些常规的清除工作，比如释放内存等。
        abort();
    }
    
    // 为什么这两个数要加在一起呢？ 纳秒不够1秒时在ts.tv_nsec上增加，一旦够1秒了，就进位到ts.tv_sec上去了。
    return SC_TICK_FROM_SEC(ts.tv_sec) + SC_TICK_FROM_NS(ts.tv_nsec);
#else
    LARGE_INTEGER c;

    // On systems that run Windows XP or later, the function will always
    // succeed and will thus never return zero.
    // <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter>
    // <https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancefrequency>

    BOOL ok = QueryPerformanceCounter(&c);
    assert(ok);
    (void) ok;

    LONGLONG counter = c.QuadPart;

    static LONGLONG frequency;
    if (!frequency) {
        // Initialize on first call
        LARGE_INTEGER f;
        ok = QueryPerformanceFrequency(&f);
        assert(ok);
        frequency = f.QuadPart;
        assert(frequency);
    }

    if (frequency % SC_TICK_FREQ == 0) {
        // Expected case (typically frequency = 10000000, i.e. 100ns precision)
        sc_tick div = frequency / SC_TICK_FREQ;
        return SC_TICK_FROM_US(counter / div);
    }

    // Split the division to avoid overflow
    sc_tick secs = SC_TICK_FROM_SEC(counter / frequency);
    sc_tick subsec = SC_TICK_FREQ * (counter % frequency) / frequency;
    return secs + subsec;
#endif
}
